/** \file
* POSIX-specific implementation of the Timer class.
* This file is part of Chorus, available at
* http://code.google.com/p/chorus-twitter/ . This file and all associated files
* and derivative works are bound by the terms of the LGPL 3.0 license,
* available at http://www.gnu.org/licenses/lgpl-3.0-standalone.html .
* \par Subversion URL:
* \$URL: http://chorus-twitter.googlecode.com/svn/branches/cpp/src/system/posix/Timer.cpp $
* \version \$Rev: 153 $
* \date    \$Date: 2010-04-06 07:43:42 +0000 (Tue, 06 Apr 2010) $
*/

#define NEEDS_POSIX
#include "config.h"

#include <iostream>
#include <signal.h>
#include <errno.h>

#ifdef __APPLE__
#	include <mach/clock.h>
#endif

#include "system/Timer.hpp"

using namespace std;

Timer::Timer()
{
#ifdef __APPLE__
	// Set up the Apple "mach" kernel clock service.
	const kern_return_t result = host_get_clock_service(mach_host_self(),
		CALENDAR_CLOCK, &hostclock);
#endif
	// Remember a timestamp of when we were instantiated.
	time0 = getTimeSpec();
}

long double Timer::getTimeDouble() const
{
	// Find the difference between now and when we were instantiated.
	const timespec time1 = getTimeSpec();
	return ((double)(time1.tv_sec - time0.tv_sec)) +
		((double)(time1.tv_nsec - time0.tv_nsec))/1000000000.0L;
}

timespec Timer::getTimeSpec() const
{
	timespec ts;
#ifdef __APPLE__
	// We're forced to do mac cruft here. Mac really fails in its promise of
	// posix compliance.
	mach_timespec_t mts;
	kern_return_t result = clock_get_time(hostclock, &mts);
	ts.tv_sec = mts.tv_sec;
	ts.tv_nsec = mts.tv_nsec;
#else
	// This works in Linux.
	if (clock_gettime(CLOCK_REALTIME, &ts))
		cerr << "clock_gettime failed, error " << errno << '\n';
#endif
	return ts;
}

timespec Timer::msToTs(unsigned ms)
{
	// Convert from milliseconds to a timespec (seconds and nanoseconds).
	timespec ts;
	ts.tv_sec = ms/1000;
	ts.tv_nsec = (ms%1000)*1000000;
	return ts;
}

timespec Timer::addSpecMs(timespec ts, unsigned ms)
{
	// Add a millisecond value to a timespec and return the sum as a timespec.
	timespec sum;
	sum.tv_sec = ts.tv_sec + ms/1000;
	sum.tv_nsec = ts.tv_nsec + ms%1000*1000000;
	sum.tv_sec += sum.tv_nsec/1000000000;
	sum.tv_nsec %= 1000000000;
	return sum;
}
